package com.wzw.expression.record.expression;

import com.wzw.expression.expression.evaluator.GeneralExpressionEvaluator;
import com.wzw.expression.expression.object.GeneralEvaluationContext;
import com.wzw.expression.expression.object.GeneralExpressionRootObject;
import com.wzw.expression.record.model.ExtraRecord;
import org.springframework.context.expression.AnnotatedElementKey;
import org.springframework.core.ParameterNameDiscoverer;
import org.springframework.expression.Expression;
import org.springframework.expression.TypeLocator;
import org.springframework.expression.spel.support.StandardTypeLocator;

import java.lang.reflect.Method;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;

/**
 * IRecord 表达式执行器
 *
 * @author Wangzhiwen
 */
public class IRecordExpressionEvaluator extends GeneralExpressionEvaluator {

    private TypeLocator typeLocator = new StandardTypeLocator();

    /**
     * 业务序列号表达式缓存
     */
    private static final Map<ExpressionKey, Expression> SERIAL_CACHE = new ConcurrentHashMap<>(64);
    /**
     * 文本内容表达式缓存
     */
    private static final Map<ExpressionKey, Expression> CONTENT_CACHE = new ConcurrentHashMap<>(64);
    /**
     * 租户表达式缓存
     */
    private static final Map<ExpressionKey, Expression> TENANT_CACHE = new ConcurrentHashMap<>(64);
    /**
     * 额外的记录表达式缓存
     */
    private static final Map<ExpressionKey, Expression> EXTRA_CACHE = new ConcurrentHashMap<>(64);

    public void setTypeLocator(TypeLocator typeLocator) {
        this.typeLocator = typeLocator;
    }

    /**
     * 序列号表达式解析
     *
     * @param serialExpression  序列号表达式
     * @param methodKey         方法
     * @param evaluationContext 上下文
     * @return serial
     */
    public String serial(String serialExpression, AnnotatedElementKey methodKey, GeneralEvaluationContext evaluationContext) {
        return resolve
                (
                        SERIAL_CACHE,
                        serialExpression,
                        methodKey,
                        evaluationContext,
                        String.class
                );
    }

    /**
     * 额外的记录表达式解析
     *
     * @param extraExpression   额外的记录表达式
     * @param methodKey         方法
     * @param evaluationContext 上下文
     * @return ExtraRecord
     */
    public ExtraRecord extra(String extraExpression, AnnotatedElementKey methodKey, GeneralEvaluationContext evaluationContext) {
        return resolve
                (
                        EXTRA_CACHE,
                        extraExpression,
                        methodKey,
                        evaluationContext,
                        ExtraRecord.class
                );
    }

    /**
     * 文本内容表达式解析
     *
     * @param textExpression    文本内容表达式
     * @param methodKey         方法
     * @param evaluationContext 上下文
     * @return content
     */
    public String content(String textExpression, AnnotatedElementKey methodKey, GeneralEvaluationContext evaluationContext) {
        return resolve
                (
                        CONTENT_CACHE,
                        textExpression,
                        methodKey,
                        evaluationContext,
                        String.class
                );
    }

    @Override
    protected GeneralExpressionRootObject getExpressionRootObject(Method method, Object[] args, Object target, Class<?> targetClass) {
        return new IRecordExpressionRootObject(method, args, target, targetClass);
    }

    @Override
    protected GeneralEvaluationContext getEvaluationContext(GeneralExpressionRootObject rootObject, Method targetMethod, Object[] args, ParameterNameDiscoverer discoverer) {
        IRecordEvaluationContext evaluationContext = new IRecordEvaluationContext(rootObject, targetMethod, args, discoverer);
        if (Objects.nonNull(typeLocator)) {
            evaluationContext.setTypeLocator(typeLocator);
        }
        return evaluationContext;
    }

    @Override
    public void clear() {
        SERIAL_CACHE.clear();
        CONTENT_CACHE.clear();
        TENANT_CACHE.clear();
        EXTRA_CACHE.clear();
    }

}
